package com.aplink.generic.util.validation;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.regex.Pattern;

import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.widget.EditText;

import com.aplink.generic.GenericFragment;
import com.aplink.generic.constant.type.ControlType;
import com.aplink.generic.constant.type.EditTextType;
import com.aplink.generic.database.DatabaseHelper;
import com.aplink.generic.database.SqlMethods;
import com.aplink.generic.google.maps.ui.LocationUtil;
import com.aplink.generic.pojo.GenericObject;
import com.aplink.generic.pojo.ObjPropInfo;
import com.aplink.generic.pojo.controlspec.AutoCompleteTextViewSpec;
import com.aplink.generic.pojo.controlspec.ButtonSpec;
import com.aplink.generic.pojo.controlspec.ControlSpec;
import com.aplink.generic.pojo.controlspec.EditTextSpec;
import com.aplink.generic.pojo.controlspec.GallerySpec;
import com.aplink.generic.util.GpsUtils;
import com.aplink.generic.util.Language;
import com.aplink.generic.widgets.ApLinkAutoCompleteTextView;
import com.aplink.generic.widgets.ApLinkBaseView;
import com.aplink.generic.widgets.ApLinkEditText;
import com.aplink.generic.widgets.ApLinkFloatingEditText;
import com.aplink.generic.widgets.ApLinkGallery;

public class ControlValidationTask extends
        AsyncTask<ControlSpec, Void, ValidationResult> implements
        LocationUtil.CallBack {
	private boolean isUpdateDistance = false;
	private ControlSpec mControlSpec = null;
	private final WeakReference<GenericFragment> mGenericFragment;
	private Location mGPSLocation = null;

	public ControlValidationTask(final GenericFragment genericFragment) {
		mGenericFragment = new WeakReference<GenericFragment>(genericFragment);
	}

	@Override
	protected ValidationResult doInBackground(ControlSpec... params) {
		mControlSpec = params[0];
		final ValidationResult mValidationResult = new ValidationResult();
		final String validationGroupName = mControlSpec.getValidationGroup();
		if (TextUtils.isEmpty(validationGroupName)) {
			mValidationResult.setValid(true);
			return mValidationResult;
		}
		boolean isValid = true;
		final ArrayList<ControlSpec> validationControls = mGenericFragment
		        .get().getValidationGroups().get(validationGroupName);
		for (final ControlSpec controlSpec : validationControls) {
			final String validationGroup = controlSpec.getValidationGroup();
			if (validationGroup == null) {
				continue;
			}
			if (!controlSpec.getValidationGroup().equalsIgnoreCase(
			        validationGroup)) {
				continue;
			}
			final ApLinkBaseView baseView = mGenericFragment.get()
			        .getAPLinkViewByID(controlSpec.getIDName());
			if (controlSpec.getControlType().equals(ControlType.EDITTEXT)
			        || controlSpec.getControlType().equals(
			                ControlType.FLOATTING_EDITTEXT)) {
				String value = "";
				if ((baseView.getAttribute() != null)
				        && (baseView.getAttribute().getValue() != null)) {
					value = baseView.getAttribute().getValue().toString();
				}
				EditText editText = null;
				if (controlSpec.getControlType().equals(ControlType.EDITTEXT)) {
					editText = ((ApLinkEditText) baseView).getView();
				} else {
					editText = ((ApLinkFloatingEditText) baseView).getView();
				}
				final EditTextSpec editTextSpecFormat = (EditTextSpec) controlSpec;
				final Validation validation = editTextSpecFormat
				        .getValidation();

				final boolean isNoBlank = validation.isNoBlank();
				final boolean isNoDuplicate = validation.isNoDuplicate();
				final int minLength = editTextSpecFormat.getMinLength();
				final int maxLength = editTextSpecFormat.getMaxLength();
				String errorMessage = null;
				if (isNoDuplicate) {
					final String whereClause = SqlMethods.getInstance()
					        .equal(controlSpec.getMappingName(), value)
					        .toString();
					final boolean isExists = DatabaseHelper.getInstance()
					        .isExists(mGenericFragment.get().getContentData(),
					                whereClause);
					if (isExists) {
						setErrorText(editText, validation.getDuplicate()
						        .getMessage());
						isValid = false;
						continue;
					}
				}
				if (isNoBlank && value.isEmpty()) {
					if (TextUtils.isEmpty(validation.getBlankRule()
					        .getMessage())) {
						errorMessage = "This field is required";
					} else {
						errorMessage = validation.getBlankRule().getMessage();
					}
					setErrorText(editText, errorMessage);
					isValid = false;
					continue;
				}
				if (value.length() < minLength) {
					setErrorText(editText, " longer than " + minLength
					        + " characters");
					isValid = false;
					continue;
				}
				if (value.length() > maxLength) {
					setErrorText(editText, " no longer than " + maxLength
					        + " characters");
					isValid = false;
					continue;
				}

				if (editTextSpecFormat.getDataInputType().equals(
				        EditTextType.EMAIL)) {
					final Pattern email = android.util.Patterns.EMAIL_ADDRESS;
					if (!email.matcher(value).matches()) {
						setErrorText(editText,
						        "This field is invalid, Please enter your email");
						isValid = false;
						continue;
					}
				}

				if (editTextSpecFormat.getDataInputType().equals(
				        EditTextType.PHONE)) {
					final Pattern email = android.util.Patterns.PHONE;
					if (!email.matcher(value).matches()) {
						setErrorText(editText,
						        "This field is invalid, Please enter phone numbers");
						isValid = false;
						continue;
					}
				}

				if (editTextSpecFormat.getDataInputType().equals(
				        EditTextType.PASSWORD)) {
					final String passwordRegex = editTextSpecFormat
					        .getRegularExpression();
					if (passwordRegex == null) {
						continue;
					}
					final Pattern password = Pattern.compile(passwordRegex);
					if (!password.matcher(value).matches()) {
						errorMessage = "Passwords must be at least 4 characters and contain at least two of the following: alphabets and numbers.";
						setErrorText(editText, errorMessage);
						isValid = false;
						continue;
					}
				}
			} else if (controlSpec.getControlType().equals(ControlType.GALLERY)) {
				GallerySpec gallerySpec = (GallerySpec) controlSpec;
				Validation validation = gallerySpec.getValidation();
				ApLinkGallery apLinkGallery = (ApLinkGallery) baseView;
				if (validation.isNoBlank()) {
					if (apLinkGallery.isEmpty()) {
						mValidationResult.setMessage(validation.getBlankRule()
						        .getMessage());
						isValid = false;
						continue;
					}
				}
			} else if (controlSpec.getControlType().equals(
			        ControlType.AUTOCOMPLETE_TEXTVIEW)) {
				AutoCompleteTextViewSpec autoCompleteTextViewSpec = (AutoCompleteTextViewSpec) controlSpec;
				Validation validation = autoCompleteTextViewSpec
				        .getValidation();
				ApLinkAutoCompleteTextView apLinkAutoCompleteTextView = (ApLinkAutoCompleteTextView) baseView;
				if (validation.isNoBlank()) {
					if (TextUtils.isEmpty(apLinkAutoCompleteTextView.getView()
					        .getText())) {
						if (TextUtils.isEmpty(validation.getBlankRule()
						        .getMessage())) {
							setErrorText(apLinkAutoCompleteTextView.getView(),
							        "This field is required");
						} else {
							setErrorText(apLinkAutoCompleteTextView.getView(),
							        validation.getBlankRule().getMessage());
						}

						isValid = false;
					}
				}
			} else if (controlSpec.getControlType().equals(ControlType.BUTTON)) {
				final ButtonSpec buttonSpec = (ButtonSpec) controlSpec;
				final Validation validation = buttonSpec.getValidation();
				if (validation.isValidLocation()) {
					final LocationManager manager = (LocationManager) mGenericFragment
					        .get().getApplicationContext()
					        .getSystemService(Context.LOCATION_SERVICE);
					if (!manager
					        .isProviderEnabled(LocationManager.GPS_PROVIDER)) {
						GpsUtils.turnGPSOn();
					}
					LocationUtil
					        .getInstance()
					        .getDefaultLocationService(
					                this,
					                mGenericFragment.get()
					                        .getApplicationContext()).connect();
					final ObjPropInfo mLatitude = mGenericFragment.get()
					        .getContentData().getLatitudeCol();
					final ObjPropInfo mLongitude = mGenericFragment.get()
					        .getContentData().getLongitudeCol();
					final GenericObject genericObject = mGenericFragment.get()
					        .getMappingObject();
					final double latitude = Double.parseDouble(genericObject
					        .getValue(mLatitude));
					final double longitude = Double.parseDouble(genericObject
					        .getValue(mLongitude));
					final Location compareLocation = new Location(
					        "compareLocation");
					compareLocation.setLatitude(latitude);
					compareLocation.setLongitude(longitude);
					boolean isChecking = true;
					while (isChecking) {
						if (LocationUtil.getInstance().isConnected()
						        && (mGPSLocation != null)) {
							if (isUpdateDistance) {
								isUpdateDistance = false;
								if (mGPSLocation.distanceTo(compareLocation) <= validation
								        .getLocationValidation().getDistance()) {
									isValid = true;
									isChecking = false;
									LocationUtil.getInstance().disconnect();
								} else {
									isValid = false;
									mValidationResult.setMessage(validation
									        .getLocationValidation()
									        .getMessage());
								}
								if (mGPSLocation.getAccuracy() <= validation
								        .getLocationValidation().getAccuracy()) {
									LocationUtil.getInstance().disconnect();
									isChecking = false;
								}
							}
						}
					}
					GpsUtils.turnGPSOff();
				}
			}
		}
		mValidationResult.setValid(isValid);
		return mValidationResult;
	}

	@Override
	public void handleLocationChange(Location location) {
		mGPSLocation = location;
		isUpdateDistance = true;
	}

	@Override
	protected void onPostExecute(final ValidationResult result) {
		mGenericFragment.get().handleValidationResult(mControlSpec, result);
		super.onPostExecute(result);
	}

	private void setErrorText(final EditText editText, final String params) {
		mGenericFragment.get().getActivity().runOnUiThread(new Runnable() {

			@Override
			public void run() {
				editText.setError(Language.getInstance().get(params));
			}
		});
	}
}
